// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "base/debug/leak_tracker.h"

#include <memory>

#include "testing/gtest/include/gtest/gtest.h"

namespace base {
namespace debug {

    namespace {

        class ClassA {
        private:
            LeakTracker<ClassA> leak_tracker_;
        };

        class ClassB {
        private:
            LeakTracker<ClassB> leak_tracker_;
        };

#ifndef ENABLE_LEAK_TRACKER

        // If leak tracking is disabled, we should do nothing.
        TEST(LeakTrackerTest, NotEnabled)
        {
            EXPECT_EQ(-1, LeakTracker<ClassA>::NumLiveInstances());
            EXPECT_EQ(-1, LeakTracker<ClassB>::NumLiveInstances());

            // Use scoped_ptr so compiler doesn't complain about unused variables.
            std::unique_ptr<ClassA> a1(new ClassA);
            std::unique_ptr<ClassB> b1(new ClassB);
            std::unique_ptr<ClassB> b2(new ClassB);

            EXPECT_EQ(-1, LeakTracker<ClassA>::NumLiveInstances());
            EXPECT_EQ(-1, LeakTracker<ClassB>::NumLiveInstances());
        }

#else

        TEST(LeakTrackerTest, Basic)
        {
            {
                ClassA a1;

                EXPECT_EQ(1, LeakTracker<ClassA>::NumLiveInstances());
                EXPECT_EQ(0, LeakTracker<ClassB>::NumLiveInstances());

                ClassB b1;
                ClassB b2;

                EXPECT_EQ(1, LeakTracker<ClassA>::NumLiveInstances());
                EXPECT_EQ(2, LeakTracker<ClassB>::NumLiveInstances());

                std::unique_ptr<ClassA> a2(new ClassA);

                EXPECT_EQ(2, LeakTracker<ClassA>::NumLiveInstances());
                EXPECT_EQ(2, LeakTracker<ClassB>::NumLiveInstances());

                a2.reset();

                EXPECT_EQ(1, LeakTracker<ClassA>::NumLiveInstances());
                EXPECT_EQ(2, LeakTracker<ClassB>::NumLiveInstances());
            }

            EXPECT_EQ(0, LeakTracker<ClassA>::NumLiveInstances());
            EXPECT_EQ(0, LeakTracker<ClassB>::NumLiveInstances());
        }

        // Try some orderings of create/remove to hit different cases in the linked-list
        // assembly.
        TEST(LeakTrackerTest, LinkedList)
        {
            EXPECT_EQ(0, LeakTracker<ClassB>::NumLiveInstances());

            std::unique_ptr<ClassA> a1(new ClassA);
            std::unique_ptr<ClassA> a2(new ClassA);
            std::unique_ptr<ClassA> a3(new ClassA);
            std::unique_ptr<ClassA> a4(new ClassA);

            EXPECT_EQ(4, LeakTracker<ClassA>::NumLiveInstances());

            // Remove the head of the list (a1).
            a1.reset();
            EXPECT_EQ(3, LeakTracker<ClassA>::NumLiveInstances());

            // Remove the tail of the list (a4).
            a4.reset();
            EXPECT_EQ(2, LeakTracker<ClassA>::NumLiveInstances());

            // Append to the new tail of the list (a3).
            std::unique_ptr<ClassA> a5(new ClassA);
            EXPECT_EQ(3, LeakTracker<ClassA>::NumLiveInstances());

            a2.reset();
            a3.reset();

            EXPECT_EQ(1, LeakTracker<ClassA>::NumLiveInstances());

            a5.reset();
            EXPECT_EQ(0, LeakTracker<ClassA>::NumLiveInstances());
        }

        TEST(LeakTrackerTest, NoOpCheckForLeaks)
        {
            // There are no live instances of ClassA, so this should do nothing.
            LeakTracker<ClassA>::CheckForLeaks();
        }

#endif // ENABLE_LEAK_TRACKER

    } // namespace

} // namespace debug
} // namespace base
